﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows;
using System.Windows.Controls;
using BindableDataGrid.Data;
using SilveR.DataValidators;
using SilveR.GUIValidators;

namespace SilveR.Views.StatsViews
{
    public partial class PowerAnalysis : UserControl, IAnalysis
    {
        private PowerAnalysisVariables powerVariables;

        public PowerAnalysis()
        {
            InitializeComponent();

            powerVariables = new PowerAnalysisVariables();
            this.DataContext = powerVariables;
        }

        private void Target_ItemDragCompleted(object sender, ItemDragEventArgs e)
        {
            //ensure that the available variables stay sorted...
            SortableObservableCollection<ColumnInfo> availableVariables = (SortableObservableCollection<ColumnInfo>)lstAvailableVariables.ItemsSource;
            availableVariables.Sort(new ColumnIndexComparer());

            //get the levels of the treatment variable and populate the control combo
            powerVariables.ControlGroupList = DataHandler.GetLevels(powerVariables.Treatment);

            //only enable the control combo if there are items in it and the analysis type is compare to control
            cboControl.IsEnabled = cboControl.Items.Count > 0;
        }

        private void oneItem_DragOverOrDrop(object sender, Microsoft.Windows.DragEventArgs e)
        {
            ListBoxDragDropTarget target = (ListBoxDragDropTarget)sender;
            ListBox lstBox = (ListBox)target.Content;

            //only allow one item in this listbox and also only if using dataset for calculations
            if (lstBox.Items.Count == 1 && powerVariables.UseDataSetValues)
            {
                e.Effects = Microsoft.Windows.DragDropEffects.None;
                e.Handled = true;
            }
        }

        private void cboData_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //refill the available variables box when the selected datahandler changes
            DataHandler dh = (DataHandler)cboData.SelectedItem;

            if (dh != null)
                powerVariables.AvailableVariables = dh.GetVariableList();
        }


        #region IAnalysis Members

        public bool ValidateGUI()
        {
            List<ValidationResult> validationResult = new List<ValidationResult>();
            bool validatedOK = Validator.TryValidateObject(powerVariables, new ValidationContext(powerVariables, null, null), validationResult, true);

            if (!validatedOK)
            {
                ValidationMessagesWindow vmw = new ValidationMessagesWindow(validationResult);
                vmw.Show();
            }

            return validatedOK;
        }

        public event DataValidatedEventHandler DataValidated;

        public void ValidateData()
        {
            PowerAnalysisValidator npv = new PowerAnalysisValidator(powerVariables);
            bool result = npv.Validate();

            if (npv.WarningMessages.Count > 0)
            {
                ValidationMessagesWindow vmw = new ValidationMessagesWindow(npv.WarningMessages);
                vmw.Closed += new EventHandler(vmw_Closed);
                vmw.Show();
            }
            else
            {
                if (DataValidated != null)
                    DataValidated(this, new DataValidatedEventArgs(result));
            }
        }

        private void vmw_Closed(object sender, EventArgs e)
        {
            ValidationMessagesWindow vmw = (ValidationMessagesWindow)sender;

            if (DataValidated != null)
                DataValidated(this, new DataValidatedEventArgs(vmw.DialogResult.Value));
        }

        public string[] ExportData()
        {
            if (this.DataHandler == null)
            {
                return null;
            }
            else
            {
                DataTable dtNew = this.DataHandler.DataTable.Copy();
                dtNew.Name = "ExportData";

                //Get the response, treatment and covariate columns by removing all other columns from the new datatable
                foreach (DataColumn col in this.DataHandler.DataTable.Columns)
                {
                    if (powerVariables.Response != col.ColumnName && powerVariables.Treatment != col.ColumnName)
                    {
                        dtNew.Columns.Remove(col);
                    }
                }

                //ensure that all data is trimmed
                dtNew.TrimAllDataInDataTable();

                //if the response is blank then remove that row
                dtNew.RemoveBlankRow(powerVariables.Response);

                //...and export them
                return dtNew.GetCSVArray();
            }
        }

        public DataHandler DataHandler
        {
            get { return powerVariables.SelectedDataHandler; }
        }

        public Dictionary<string, string> GetAnalysisArguments()
        {
            Dictionary<string, string> arguments = new Dictionary<string, string>();

            if (powerVariables.UseSuppliedValues)
            {
                arguments.Add("Value Type", "SuppliedValues"); //4
                arguments.Add("Group Mean", ArgumentConverters.GetNULLOrText(powerVariables.GroupMean)); //5

                if (powerVariables.UseStandardDeviation)
                {
                    arguments.Add("Variance Type", "StandardDeviation"); //6
                    arguments.Add("Standard Deviation", powerVariables.StandardDeviation); //7
                }
                else if (powerVariables.UseVariance)
                {
                    arguments.Add("Variance Type", "Variance"); //6
                    arguments.Add("Variance", powerVariables.Variance); //7
                }
            }
            else if (powerVariables.UseDataSetValues)
            {
                arguments.Add("Value Type", "DatasetValues"); //4
                arguments.Add("Response", ArgumentConverters.ConvertIllegalChars(powerVariables.Response)); //5

                arguments.Add("Treatment", ArgumentConverters.GetNULLOrText(powerVariables.Treatment));

                arguments.Add("Control", ArgumentConverters.GetNULLOrText(powerVariables.ControlGroup)); //7
            }

            arguments.Add("Significance Level", powerVariables.Significance); //8

            if (powerVariables.UsePercentChange)
            {
                arguments.Add("Delta Type", "Percent"); //9
                arguments.Add("Percent", powerVariables.PercentChange); //10
            }
            else if (powerVariables.UseAbsoluteChange)
            {
                arguments.Add("Delta Type", "Absolute"); //9
                arguments.Add("Absolute", powerVariables.AbsoluteChange); //10
            }

            if (powerVariables.UseCustomSampleSize || powerVariables.UseSampleSize6To15)
            {
                arguments.Add("Plot Axis", "SampleSize"); //11
                if (powerVariables.UseSampleSize6To15)
                {
                    arguments.Add("Sample Size From", "6"); //12
                    arguments.Add("Sample Size To", "15"); //13
                }
                else if (powerVariables.UseCustomSampleSize)
                {
                    arguments.Add("Sample Size From", powerVariables.SampleSizeFrom); //12
                    arguments.Add("Sample Size To", powerVariables.SampleSizeTo); //13
                }
            }
            else if (powerVariables.UsePower)
            {
                arguments.Add("Plot Axis", "PowerAxis"); //11
                if (powerVariables.UseTargetPower)
                {
                    arguments.Add("Power From", "70"); //12
                    arguments.Add("Power To", "90"); //13
                }
                else if (powerVariables.UseCustomPower)
                {
                    arguments.Add("Power From", powerVariables.PowerFrom); //12
                    arguments.Add("Power To", powerVariables.PowerTo); //13
                }
            }

            arguments.Add("Graph Title", ArgumentConverters.GetNULLOrText(powerVariables.GraphTitle)); //14

            return arguments;
        }

        public string AnalysisName
        {
            get { return "Power Analysis"; }
        }

        public string ScriptName
        {
            get { return "PowerAnalysis.R"; }
        }

        public void UpdateDataHandlers()
        {
            //remember the current data handler before updating the available variables so that it can be selected later...
            DataHandler currentDataHandler = powerVariables.SelectedDataHandler;

            App app = (App)Application.Current;
            powerVariables.AvailableDataHandlers = app.DataHandlers;

            //reselect the current handler (if any)
            powerVariables.SelectedDataHandler = currentDataHandler;
        }

        #endregion
    }


    public class PowerAnalysisVariables : INotifyPropertyChanged
    {
        public PowerAnalysisVariables()
        {
            App app = (App)Application.Current;
            availableDataHandlers = app.DataHandlers;
        }

        private bool useSuppliedValues = true;
        public bool UseSuppliedValues
        {
            get { return useSuppliedValues; }
            set
            {
                useSuppliedValues = value;
                NotifyPropertyChanged("UseSuppliedValues");
            }
        }

        private string groupMean;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateGroupMean")]
        public string GroupMean
        {
            get { return groupMean; }
            set { groupMean = value; }
        }

        private bool useStandardDeviation = true;
        public bool UseStandardDeviation
        {
            get { return useStandardDeviation; }
            set
            {
                useStandardDeviation = value;
            }
        }

        private string standardDeviation;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateStandardDeviation")]
        public string StandardDeviation
        {
            get { return standardDeviation; }
            set { standardDeviation = value; }
        }

        private bool useVariance;
        public bool UseVariance
        {
            get { return useVariance; }
            set { useVariance = value; }
        }

        private string variance;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateVariance")]
        public string Variance
        {
            get { return variance; }
            set { variance = value; }
        }

        private bool useDataSetValues = false;
        public bool UseDataSetValues
        {
            get { return useDataSetValues; }
            set
            {
                useDataSetValues = value;
                NotifyPropertyChanged("UseDataSetValues");
            }
        }


        private ObservableCollection<DataHandler> availableDataHandlers = new ObservableCollection<DataHandler>();
        public ObservableCollection<DataHandler> AvailableDataHandlers
        {
            get
            {
                return availableDataHandlers;
            }

            set
            {
                availableDataHandlers = value;
                NotifyPropertyChanged("AvailableDataHandlers");
            }
        }

        private DataHandler selectedDataHandler;
        public DataHandler SelectedDataHandler
        {
            get { return selectedDataHandler; }
            set
            {
                selectedDataHandler = value;
                NotifyPropertyChanged("SelectedDataHandler");
            }
        }


        private ObservableCollection<ColumnInfo> availableVariables = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> AvailableVariables
        {
            get { return availableVariables; }
            set
            {
                availableVariables = value;
                NotifyPropertyChanged("AvailableVariables");
            }
        }

        private ObservableCollection<ColumnInfo> responseList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> ResponseList
        {
            get { return responseList; }
            set
            {
                responseList[0] = value[0];
                NotifyPropertyChanged("ResponseList");
            }
        }

        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateResponseOrTreatment")]
        [Display(Name = "Response")]
        public string Response
        {
            get
            {
                if (responseList.Count == 1)
                    return responseList[0].ColumnName;
                else
                    return null;
            }
        }

        private ObservableCollection<ColumnInfo> treatmentList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> TreatmentList
        {
            get { return treatmentList; }
            set
            {
                treatmentList[0] = value[0];
                NotifyPropertyChanged("TreatmentList");
            }
        }

        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateResponseOrTreatment")]
        [Display(Name = "Treatment")]
        public string Treatment
        {
            get
            {
                if (treatmentList.Count == 1)
                    return treatmentList[0].ColumnName;
                else
                    return null;
            }
        }

        private string significance = "0.05";
        public string Significance
        {
            get { return significance; }
            set { significance = value; }
        }

        private List<string> significanceList = new List<string>() { "0.1", "0.05", "0.01", "0.001" };
        public List<string> SignificanceList
        {
            get { return significanceList; }
        }

        private string controlGroup;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateControlGroup")]
        public string ControlGroup
        {
            get { return controlGroup; }
            set { controlGroup = value; }
        }

        private List<string> controlGroupList = new List<string>();
        public List<string> ControlGroupList
        {
            get { return controlGroupList; }
            set
            {
                controlGroupList = value;
                NotifyPropertyChanged("ControlGroupList");
            }
        }

        private bool usePercentChange = true;
        public bool UsePercentChange
        {
            get { return usePercentChange; }
            set
            {
                usePercentChange = value;
                NotifyPropertyChanged("UsePercentChange");
            }
        }

        private string percentChange = String.Empty;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateChanges")]
        [Display(Name = "Percent Change")]
        public string PercentChange
        {
            get { return percentChange.Replace(" ", ""); }
            set { percentChange = value; }
        }


        private bool useAbsoluteChange;
        public bool UseAbsoluteChange
        {
            get { return useAbsoluteChange; }
            set
            {
                useAbsoluteChange = value;
                NotifyPropertyChanged("UseAbsoluteChange");
            }
        }

        private string absoluteChange = String.Empty;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateChanges")]
        [Display(Name = "Absolute Change")]
        public string AbsoluteChange
        {
            get { return absoluteChange.Replace(" ", ""); }
            set { absoluteChange = value; }
        }

        private string sampleSizeFrom;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateSampleSizeFrom")]
        public string SampleSizeFrom
        {
            get { return sampleSizeFrom; }
            set { sampleSizeFrom = value; }
        }

        private string sampleSizeTo;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateSampleSizeTo")]
        public string SampleSizeTo
        {
            get { return sampleSizeTo; }
            set { sampleSizeTo = value; }
        }

        private string powerFrom;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateCustomFrom")]
        public string PowerFrom
        {
            get { return powerFrom; }
            set { powerFrom = value; }
        }

        private string powerTo;
        [CustomValidation(typeof(PowerAnalysisGUIValidator), "ValidateCustomTo")]
        public string PowerTo
        {
            get { return powerTo; }
            set { powerTo = value; }
        }

        private string graphTitle;
        public string GraphTitle
        {
            get { return graphTitle; }
            set { graphTitle = value; }
        }

        private bool useCustomSampleSize;
        public bool UseCustomSampleSize
        {
            get { return useCustomSampleSize; }
            set
            {
                useCustomSampleSize = value;
                NotifyPropertyChanged("UseCustomSampleSize");
            }
        }

        private bool useSampleSize6To15 = true;
        public bool UseSampleSize6To15
        {
            get { return useSampleSize6To15; }
            set { useSampleSize6To15 = value; }
        }

        private bool usePower;
        public bool UsePower
        {
            get { return usePower; }
            set
            {
                usePower = value;
                NotifyPropertyChanged("UsePower");
            }
        }

        private bool useTargetPower = true;
        public bool UseTargetPower
        {
            get { return useTargetPower; }
            set { useTargetPower = value; }
        }

        private bool useCustomPower;
        public bool UseCustomPower
        {
            get { return useCustomPower; }
            set { useCustomPower = value; }
        }

        
        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event, passing the source property that is being updated.
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}